
package com.gitee.jmash.oidc.web.controller;

import java.io.IOException;
import java.io.StringReader;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import com.gitee.jmash.common.event.SafeEvent;
import com.gitee.jmash.oidc.web.Constant;
import com.gitee.jmash.oidc.web.models.LoginModel;
import com.gitee.jmash.oidc.web.models.LoginRedirect;
import com.gitee.jmash.oidc.web.models.RedirectResult;
import com.gitee.jmash.rbac.RbacFactory;
import com.gitee.jmash.rbac.client.shiro.authc.JmashShiroJwtToken;
import com.gitee.jmash.rbac.entity.TokenEntity;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.mvc.Controller;
import jakarta.mvc.Models;
import jakarta.mvc.MvcContext;
import jakarta.mvc.binding.BindingResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Form;
import jakarta.ws.rs.core.Response;
import jmash.rbac.protobuf.LoginReq;

/** Login . */
@Path("login")
@Controller
@RequestScoped
public class LoginController {

  @Inject
  private Models models;
  @Inject
  private MvcContext mvc;
  @Inject
  private BindingResult bindingResult;
  /** Login Redirect @RedirectScoped. */
  @Inject
  private LoginRedirect loginRedirect;
  /** Base Redirect @RedirectScoped. */
  @Inject
  private RedirectResult redirectResult;
  /** 安全日志. */
  @Inject
  Event<SafeEvent> event;

  /** Login View. */
  @GET
  public String execute(@QueryParam("backurl") String backurl, @Context HttpServletRequest req,
      @Context HttpServletResponse resp) throws IOException {
    if (StringUtils.isNotBlank(backurl)) {
      models.put("backurl", backurl);
    }
    if (StringUtils.isNotBlank(loginRedirect.getBackurl())) {
      models.put("backurl", loginRedirect.getBackurl());
    }

    Subject subject = SecurityUtils.getSubject();
    if (subject.isAuthenticated() || subject.isRemembered()) {
      return loginSuccess(backurl, req, resp);
    }

    return "login.ftl";
  }

  /** Login POST. */
  @POST
  public String login(@Valid @BeanParam LoginModel loginModel,
      @FormParam("h-captcha-response") String captcha, @FormParam("backurl") String backurl,
      @Context HttpServletRequest req, @Context HttpServletResponse resp) throws IOException {
    loginRedirect.setBackurl(backurl);
    if (bindingResult.isFailed()) {
      redirectResult.addBindingResult(bindingResult);
      return "redirect:/oauth/login";
    }

    // 行为验证码.
    if (!captcha(captcha)) {
      redirectResult.addError("login.error", "验证码未通过，请重试！");
      return "redirect:/oauth/login";
    }

    LoginReq loginReq = LoginReq.newBuilder().setTenant(Constant.TENANT).setDirectoryId("jmash")
        .setUserName(loginModel.getUsername()).setEncodePwd(loginModel.getPassword()).setClientId("oidc").build();
    TokenEntity tokenEntity = RbacFactory.getUserWrite(Constant.TENANT).login(loginReq);
    
    Subject subject = SecurityUtils.getSubject();
    JmashShiroJwtToken token = new JmashShiroJwtToken(tokenEntity.getAccessToken());
    //token.setRememberMe(loginModel.isRemember());

    try {
      subject.login(token);
    } catch (Exception ex) {
      redirectResult.addError("login.error", "");
      event.fireAsync(SafeEvent.login(loginModel.getUsername(),"", false));
      return "redirect:/oauth/login";
    }
    event.fireAsync(SafeEvent.login(loginModel.getUsername(),"", true));
    return loginSuccess(backurl, req, resp);
  }

  /** Login Success Redirect. */
  public String loginSuccess(String backurl, HttpServletRequest req, HttpServletResponse resp)
      throws IOException {
    SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(req);
    // Redirect backurl.
    if (StringUtils.isNotBlank(backurl)) {
      resp.sendRedirect(backurl);
      return null;
    } else if (savedRequest != null
        && savedRequest.getMethod().equalsIgnoreCase(AccessControlFilter.GET_METHOD)) {
      resp.sendRedirect(savedRequest.getRequestUrl());
      return null;
    } else {
      resp.sendRedirect(mvc.uri("DefaultController#execute").toString());
      return null;
    }
  }

  /**
   * hcaptcha 校验验证码.
   */
  public boolean captcha(String captcha) {
    Form form = new Form().param("secret", "ES_e80e46f5e5094205ac73e5a3ec780bb0").param("response",
        captcha);
    WebTarget target = ClientBuilder.newClient().target("https://hcaptcha.com/siteverify");
    Response res = target.request().post(Entity.form(form));
    if (res.getStatus() != 200) {
      return false;
    }
    String result = res.readEntity(String.class);
    JsonReader jsonReader = Json.createReader(new StringReader(result));
    JsonObject object = jsonReader.readObject();
    return object.getBoolean("success");
  }

}
